package com.xiaode;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jfinal.config.Constants;
import com.jfinal.config.Handlers;
import com.jfinal.config.Interceptors;
import com.jfinal.config.JFinalConfig;
import com.jfinal.config.Plugins;
import com.jfinal.config.Routes;
import com.jfinal.core.Controller;
import com.jfinal.json.MixedJsonFactory;
import com.jfinal.kit.Kv;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.cron4j.Cron4jPlugin;
import com.jfinal.plugin.hikaricp.HikariCpPlugin;
import com.jfinal.plugin.redis.Redis;
import com.jfinal.plugin.redis.RedisPlugin;
import com.jfinal.server.undertow.UndertowServer;
import com.jfinal.template.Engine;
import com.xiaode.common.JwtHelper;
import com.xiaode.common.MyRenderFactory;
import com.xiaode.common.SysInit;
import com.xiaode.common.anotation.Authority;
import com.xiaode.common.constant.Const;
import com.xiaode.common.model._MappingKit;
import com.xiaode.common.routes.AdminRoutes;
import com.xiaode.common.routes.AppRoutes;
import com.xiaode.common.routes.OtherRoutes;
import com.xiaode.controller.base.BaseController;
import com.xiaode.controller.callBack.HikvisionReturnController;
import com.xiaode.controller.callBack.ReturnController;
import com.xiaode.controller.test.TestController;
import com.xiaode.exce.GlobalExceptionInterceptor;
import com.xiaode.interceptor.AccessAllowInterceptor;
import com.xiaode.plugin.SensitiveWord.SensitiveWordPlugin;
import com.xiaode.plugin.influx.InFluxDbPlugin;
import com.xiaode.plugin.obs.ObsPlugin;
import com.xiaode.plugin.threadpool.ThreadPoolPlugin;
import com.xiaode.util.hikvision.HikvisionUtil;
import com.xiaode.validator.ParameterValidator;
import com.xiaode.validator.SensitiveWordValidator;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class XiaoDeConfig extends JFinalConfig {
	
	//存放所有权限点
	public static Set<String> allPermPoint = new HashSet<String>();

	static Prop p;
	
	/**
	 * 启动入口，运行此 main 方法可以启动项目，此 main 方法可以放置在任意的 Class 类定义中，不一定要放于此
	 */
	public static void main(String[] args) {
		UndertowServer.start(XiaoDeConfig.class, 1234, true);
	}
	
	/**
	 * PropKit.useFirstFound(...) 使用参数中从左到右最先被找到的配置文件
	 * 从左到右依次去找配置，找到则立即加载并立即返回，后续配置将被忽略
	 */
	static void loadConfig() {
		if (p == null) {
			p = PropKit.useFirstFound("config-pro.txt", "config-dev.txt");
		}
	}
	
	/**
	 * 配置常量
	 */
	@Override
	public void configConstant(Constants me) {
		loadConfig();
		
		me.setDevMode(p.getBoolean("devMode", false));//
		// 设置上传文件大小
		me.setMaxPostSize(1024 * 1024 * 200);
		/**
		 * 支持 Controller、Interceptor、Validator 之中使用 @Inject 注入业务层，并且自动实现 AOP
		 * 注入动作支持任意深度并自动处理循环注入
		 */
		me.setInjectDependency(true);
		// 配置对超类中的属性进行注入
		me.setInjectSuperClass(true);
		
		me.setJsonFactory(new MixedJsonFactory());
		// 替换部分返回
		me.setRenderFactory(new MyRenderFactory());
		
		me.setBaseUploadPath("upload");
	}
	
	/**
	 * 配置路由
	 */
	@Override
	public void configRoute(Routes me) {
		
		me.add("/test", TestController.class);
		
		me.add("/callback", ReturnController.class);
		me.add("/callbackHK", HikvisionReturnController.class);
		me.add("/base", BaseController.class);
		
		me.add(new AdminRoutes());//外面的拦截器不会对里面的有影响
        me.add(new AppRoutes());
        me.add(new OtherRoutes());
	}
	
	/**
	 * 配置全局拦截器
	 */
	@Override
	public void configInterceptor(Interceptors me) {
		//me.add(new CORSInterceptor());官方的不好用
		me.add(new AccessAllowInterceptor());
		me.add(new SensitiveWordValidator());
		me.add(new ParameterValidator());
//		me.addGlobalActionInterceptor(new GlobalExceptionInterceptor());
	}
	
	@Override
	public void configEngine(Engine me) {
		
	}
	
	/**
	 * 配置插件
	 */
	@Override
	public void configPlugin(Plugins me) {
		
  		//redis
        RedisPlugin redisPlugin = new RedisPlugin(Const.appRedis, p.get("redisUrl"), p.getInt("redisPort"), 5000, p.get("redisPassword"), p.getInt("database"));
//        redisPlugin.setSerializer(JdkSerializer.me); // 需要使用fst高性能序列化的用户请删除这一行（Fst jar依赖请查看WIKI）
		me.add(redisPlugin);
        
  		HikariCpPlugin dbPool = new HikariCpPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
		me.add(dbPool);
  		ActiveRecordPlugin arp = new ActiveRecordPlugin(dbPool);
  		arp.addSqlTemplate("all.sql");
  		_MappingKit.mapping(arp);
		arp.setDevMode(true);
//		arp.setShowSql(true);
  		me.add(arp);
  		
  		//线程池
        ThreadPoolPlugin threadPoolPlugin = new ThreadPoolPlugin(10,20,10,5);
        me.add(threadPoolPlugin);

  		//定时器
  		Cron4jPlugin cp = new Cron4jPlugin("cron4j.txt");
  		me.add(cp);
        
  		//敏感词
  		SensitiveWordPlugin sw = new SensitiveWordPlugin();
  		me.add(sw);

		//obs
		ObsPlugin obs = new ObsPlugin("obs.properties");
		me.add(obs);

		InFluxDbPlugin in = new InFluxDbPlugin("influxdb.properties");
		me.add(in);
	}
	
	/**
	 * 配置处理器
	 */
	@Override
	public void configHandler(Handlers me) {
		
	}
	
	// 系统启动完成后回调
	@Override
	public void onStart() {
		//初始话参数
		SysInit sysInit = new SysInit();
		sysInit.init();
		getPermPointTree();
		
		//读取密钥文件
		FileReader reader = new FileReader(PathKit.getWebRootPath() + "/serviceKey.txt");
    	List<String> fs = reader.readLines();
    	for(String f : fs) {
    		JSONObject jo = JSONUtil.parseObj(f);
        	String salt = Const.serverConst.serviceSalt;
        	String accessKey = jo.getStr("accessKey");
        	String secretKey = jo.getStr("secretKey");
        	String serviceId = jo.getStr("serviceId");
        	byte[] key = (secretKey + salt).getBytes();
        	HMac mac = new HMac(HmacAlgorithm.HmacMD5, key);
        	String macHex1 = mac.digestHex(accessKey);
        	Redis.use().set(Const.serverConst.serviceSecretKey + serviceId, macHex1);
    	}
    	System.err.println("| 读取密钥文件完成");
		
		try {
//			HikvisionUtil.getAllCamera(1, 100);
			System.err.println("| 摄像头同步完成");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		try {
//			WeLinkUtil.initAllRole();
//			WeLinkUtil.initAllUser();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	public void getPermPointTree() {
		//获取所有权限点
		List<Kv> kvs = CollectionUtil.newArrayList();
		Set<Class<?>> controllerClasses = ClassScanner.scanPackage("com.xiaode.controller.admin", aClass -> Controller.class.isAssignableFrom(aClass));
        if (controllerClasses.isEmpty()) {
            return;
        }
        for(Class<?> controllerClass : controllerClasses) {
        	List<Method> methods = ReflectUtil.getPublicMethods(controllerClass,
                    method -> method.getAnnotation(Authority.class) != null && Modifier.isPublic(method.getModifiers()));
        	for (Method method : methods) {
        		Authority authority = method.getAnnotation(Authority.class);
        		String permission = authority.permission();
        		if (!StrUtil.isEmpty(permission)) {
        			allPermPoint.add(permission);

        	    	Kv kv = Kv.create();
        	    	kv.set("parentName", authority.permissionParentName());
        	    	kv.set("name", authority.permissionName());
        	    	kv.set("description", authority.description());
        	    	kv.set("permission", authority.permission());
        	    	kvs.add(kv);
        		}
            }
        }
        // 权限树
        Map<String ,Kv> parent = new HashMap<String ,Kv>();
        for(Kv kv : kvs) {
        	String parentName = kv.getStr("parentName");
        	String name = kv.getStr("name");
        	String description = kv.getStr("description");
        	String permission = kv.getStr("permission");
        	if(parent.containsKey(parentName)) { //存在parentName
        		Kv kvVo = parent.get(parentName);
        		List<Kv> childrens = kvVo.getAs("children");
        		boolean flag = false;
        		for(Kv children : childrens) {
        			String cid = children.getStr("id");
        			if(StrUtil.equals(cid, name)) { //存在Name
        				List<Kv> cchildrens = children.getAs("children");
        				flag = true;
        				boolean c = false;
        				for(Kv cchildren : cchildrens) {
        					String ccid = cchildren.getStr("id");
        					if(StrUtil.equals(ccid, description)) { //存在description
        						c = true;
        						break;
        					}
        				}
        				if(!c) {
        					Kv kvcc = Kv.create();
	        				kvcc.set("id", description);
	        				kvcc.set("label", description);
	        				kvcc.set("api", permission);
	        				cchildrens.add(kvcc);
        				}
        			}
        		}
        		if (!flag) {
					Kv kvc = Kv.create();
					kvc.set("id", name);
					kvc.set("label", name);
					List<Kv> cchildrens = CollectionUtil.newArrayList();
					Kv kvcc = Kv.create();
					kvcc.set("id", name);
					kvcc.set("label", name);
					kvcc.set("api", permission);
					cchildrens.add(kvcc);
					kvc.set("children", cchildrens);
					childrens.add(kvc);
				}
    		}else {
    			Kv kvVo = Kv.create();
    			kvVo.set("id", parentName);
    			kvVo.set("label", parentName);
        		List<Kv> childrens = CollectionUtil.newArrayList();

        		Kv kvc = Kv.create();
        		kvc.set("id", name);
        		kvc.set("label", name);
        		List<Kv> cchildrens = CollectionUtil.newArrayList();

        		Kv kvcc = Kv.create();
        		kvcc.set("id", name);
        		kvcc.set("label", name);
        		kvcc.set("api", permission);
        		cchildrens.add(kvcc);
        		kvc.set("children", cchildrens);

        		childrens.add(kvc);
        		kvVo.set("children", childrens);

        		parent.put(parentName, kvVo);
    		}
        }

        List<Kv> permPointTree = CollectionUtil.newArrayList(parent.values());
        Redis.use().set("permPointTree", permPointTree);
        Redis.use().set("allPermPoint", allPermPoint);
//        System.out.println(permPointTree);
	}


}
